﻿
using System;
using System.Collections;
using System.Collections.Generic;

public class SnapInfoMgr
{
    private SnapInfoMgr() { }
    private static SnapInfoMgr _instance;
    public static SnapInfoMgr Instance
    {
        get
        {
            if (null == _instance)
            {
                _instance = new SnapInfoMgr();
            }
            return _instance;
        }
    }

    private List<SnapInfo> _snapInfos = new List<SnapInfo>();
    private int _curIndex = 0;
    public void AddSnapInfo(SnapInfo snapInfo)
    {
//        snapInfo.Index = _snapInfos.Count;
        _snapInfos.Add(snapInfo);
    }

    public SnapInfo GetSnapInfoByIndex(int index)
    {
        if (index < 0 || index > _snapInfos.Count) return null;
        return _snapInfos[index];
    }

    public void SetCurrentIndex(int index)
    {
        _curIndex = index;
    }

    public SnapInfo GetCurrentSnapInfo()
    {
        return GetSnapInfoByIndex(_curIndex);
    }

    public void Clear()
    {
        _snapInfos.Clear();
    }
}


public class SnapInfo
{
    private DebugData _debugData;
    private LuaObject _registerLuaObject;
    private Dictionary<string, LuaObject> _luaObjectDic = new Dictionary<string, LuaObject>();
    private List<LuaObject> _bfsLuaObjects = new List<LuaObject>();

    public SnapInfo(DebugData debugData)
    {
        _debugData = debugData;

        // 生成luaobject
        // 添加信息
        foreach (var pair in _debugData.ObjectDic)
        {
            _luaObjectDic[pair.Key] = new LuaObject(pair.Key, pair.Value);
        }

        // BFS生成从register开始遍历
        _registerLuaObject = _luaObjectDic[_debugData.RegisterAddress];
        _registerLuaObject.NodeStr = "Reg";
        GenrateBFS();
    }

    public Dictionary<string, LuaObject> GetLuaObjectDic()
    {
        return _luaObjectDic;
    }

    public LuaObject GetLuaObject(string address)
    {
        if(_luaObjectDic.ContainsKey(address))
            return _luaObjectDic[address];
        return null;
    }

    public DebugData GetDebugData()
    {
        return _debugData;
    }

    public void GenrateBFS()
    {
        Queue<LuaObject> queue = new Queue<LuaObject>();
        queue.Enqueue(_registerLuaObject);
        BFS(_registerLuaObject, _bfsLuaObjects, queue);
    }

    public List<LuaObject> GetBFS()
    {
        return _bfsLuaObjects;
    }

    public void BFS(LuaObject luaObject, List<LuaObject> bfsLuaObjects, Queue<LuaObject> queue)
    {
        while (queue.Count != 0)
        {
            LuaObject topObject = queue.Dequeue();

            bfsLuaObjects.Add(topObject);

            Dictionary<string, string> childs = topObject.GetChilds();
            foreach (var key in childs.Keys)
            {
                LuaObject child = _luaObjectDic[childs[key]];
                if (child.Marked == false)
                {
                    child.Marked = true;
                    child.NodeStr = topObject.NodeStr + "." + key;
                    queue.Enqueue(child);
                }
            }

            Dictionary<string, string> upvalues = topObject.GetUpvalues();
            foreach (var key in upvalues.Keys)
            {
                LuaObject child = _luaObjectDic[upvalues[key]];
                if (child.Marked == false)
                {
                    child.Marked = true;
                    child.NodeStr = topObject.NodeStr + ".Upvalue." + key;
                    queue.Enqueue(child);
                }
            }

        }
    }

}


public class LeakLuaObject
{
    private List<LuaObject> _luaObjects;

    public string Address { get; set; }
    public string NodeStr { get; set; }

    // 引用处
    public List<int> RefList = new List<int>();

    // Table有几个元素
    public List<int> ChildCountList = new List<int>();
    public List<double> SizeList = new List<double>();

    public string SizeChangeStr { get; set; }

    public int Type { get; set; }

    public LeakLuaObject(List<LuaObject> luaObjects)
    {
        _luaObjects = luaObjects;
        Address = luaObjects[0].Address;
        NodeStr = luaObjects[0].NodeStr;
        Type = luaObjects[0].Type;

        for (int i = luaObjects.Count - 1; i >= 0; i--)
        {
            RefList.Add(luaObjects[i].Ref);
            ChildCountList.Add(luaObjects[i].ChildCount);
            SizeList.Add(luaObjects[i].Size);
        }
    }

    public bool CheckRefAdd()
    {
        bool mayLeak = true;
        if (RefList[RefList.Count - 1] == RefList[0]) return false;
        for (int i = 0; i < RefList.Count - 1; i++)
        {
            if (RefList[i + 1] < RefList[i])
            {
                mayLeak = false;
            }  
        }
        return mayLeak;
    }

    public bool CheckChildCountAdd()
    {
        bool mayLeak = true;
        if (ChildCountList[ChildCountList.Count - 1] == ChildCountList[0]) return false;
        for (int i = 0; i < ChildCountList.Count - 1; i++)
        {
            if (ChildCountList[i + 1] < ChildCountList[i])
            {
                mayLeak = false;
                break;
            }
        }
        return mayLeak;
    }

    public bool HighRiskCountAdd()
    {
        bool mayLeak = true;
        for (int i = 0; i < ChildCountList.Count - 1; i++)
        {
            if (ChildCountList[i + 1] <= ChildCountList[i])
            {
                mayLeak = false;
            }
        }
        return mayLeak;
    }

    public bool CheckSizeAdd()
    {
        bool mayLeak = true;
        if (Math.Abs(SizeList[SizeList.Count - 1] - SizeList[0]) < 0.00001 ) return false;
        for (int i = 0; i < SizeList.Count - 1; i++)
        {
            if (SizeList[i + 1] < SizeList[i])
            {
                mayLeak = false;
            }
        }
        return mayLeak;
    }

    public string GetRefStr()
    {
        string str = "";
        for (int i = 0; i < RefList.Count; i++)
        {
            str += RefList[i] + "->";
        }
        return str;
    }

    public string GetSizeStr()
    {
        string str = "";
        for (int i = 0; i < SizeList.Count; i++)
        {
            str += SizeList[i] + "->";
        }
        return str;
    }

    public string GetChildCountStr()
    {
        string str = "";
        for (int i = 0; i < ChildCountList.Count; i++)
        {
            str += ChildCountList[i] + "->";
        }
        return str;
    }
}

public class LeakSnapContainer
{
    private List<SnapInfo> _snapInfos = new List<SnapInfo>();
    private List<LeakLuaObject> _leakLuaObjects = new List<LeakLuaObject>();

    public void Add(SnapInfo snapInfo)
    {
        _snapInfos.Add(snapInfo);
    }

    public List<LeakLuaObject> GetLeakLuaObjects()
    {
        return _leakLuaObjects;
    }

    public List<LeakLuaObject> GenerateLeakLuaObjects()
    {
        SnapInfo lastSnapInfo = _snapInfos[_snapInfos.Count - 1];
        Dictionary<string, LuaObject> lastLuaObjectDic = lastSnapInfo.GetLuaObjectDic();
        _leakLuaObjects = new List<LeakLuaObject>();
        foreach (var pair in lastLuaObjectDic)
        {
            List<LuaObject> luabObjects = new List<LuaObject>();
            for (int i = _snapInfos.Count - 1; i >= 0; i --)
            {
                LuaObject luaObject = _snapInfos[i].GetLuaObject(pair.Key);
                if (luaObject != null)
                {
                    luabObjects.Add(luaObject);
                }
                else
                {
                    break;
                }
            }
            LeakLuaObject leakLuaObject = new LeakLuaObject(luabObjects);
            if (leakLuaObject.CheckRefAdd() || leakLuaObject.CheckChildCountAdd() || leakLuaObject.CheckSizeAdd())
            {
                _leakLuaObjects.Add(leakLuaObject);
            }
        }
        return _leakLuaObjects;

    }
}